//
//  SLDebuger.m
//  Objective-C
//
//  Created by S_Line on 2018/8/31.
//  Copyright © 2018年 S_Line. All rights reserved.
//

#import "SLDebuger.h"

#import "SLRouteDebugView.h"
#import "SLLogger.h"

@class SLDebugMenuItem;

@interface SLDebuger()<CAAnimationDelegate>

// data
@property (nonatomic, assign) CGFloat screenWidth;
@property (nonatomic, assign) CGFloat screenHeight;
@property (nonatomic, assign) CGRect debugerFrame;
@property (nonatomic, strong) NSMutableArray *menuItems;
@property (nonatomic, copy) void(^hideBlock)(void);

// views
@property (nonatomic, weak) UIView *currentView;
@property (nonatomic, strong) UIView *backView;
@property (nonatomic, weak) UIView *menuBackView;
@property (nonatomic, strong) UIButton *entranceButton;
@property (nonatomic, weak) UIView *subMenuView;

// layer
@property (nonatomic, weak) CAShapeLayer *backMaskLayer;

@end

@implementation SLDebuger

+ (instancetype)sharedInstance {
    static SLDebuger *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (sharedInstance == nil) {
            sharedInstance = [[SLDebuger alloc] init];
        }
    });
    return sharedInstance;
}

- (instancetype)init {
    if (self) {
        [self initData];
        [self initViews];
    }
    return self;
}

- (void)initData {
    __weak typeof(self) weakself = self;
    
    self.screenWidth = [UIScreen mainScreen].bounds.size.width;
    self.screenHeight = [UIScreen mainScreen].bounds.size.height;
    self.debugerFrame = CGRectMake(0, _screenHeight - 100.0, 100, 40);
    
    self.menuItems = [NSMutableArray new];
    [self.menuItems addObject:[SLDebugMenuItem itemWithMenuTitle:@"测试路由" action:^id{
        return [SLRouteDebugView defaultView];
    }]];
    [self.menuItems addObject:[SLDebugMenuItem itemWithMenuTitle:@"日志" action:^id{
        return [[SLLogger sharedInstance] resetLogView];
    }]];
    
    self.hideBlock = ^{
        [weakself hideBackView];
    };
}

- (void)initViews {
    __weak typeof(self) weakself = self;
    
    UIButton *entranceButton = [[UIButton alloc] initWithFrame:self.debugerFrame];
    self.entranceButton = entranceButton;
    [self setButton:entranceButton withTitle:@"调试器" titleColor:[UIColor whiteColor] backgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.7] target:self action:@selector(entranceButtonClick:)];
    
    UIView *backView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.backView = backView;
    backView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6];
    backView.tapBlock = ^{
        if (weakself.subMenuView) {
            [weakself hideMenuView:weakself.subMenuView];
        } else {
            [weakself hideBackView];
        }
    };
    
    UIView *menuBackView = [[UIView alloc] initWithFrame:backView.bounds];
    self.menuBackView = menuBackView;
    [backView addSubview:menuBackView];
    
    for (int i = 0; i < _menuItems.count; i++) {
        SLDebugMenuItem *item = _menuItems[i];
        
        UIView *menuItemView = [self renderMenuItemViewWithMenuItem:item index:i];
        
        menuItemView.tapBlock = ^{
            UIView *subMenuView = item.action();
            if (subMenuView && [subMenuView isKindOfClass:[UIView class]]) {
                weakself.menuBackView.alpha = 0;
                subMenuView.center = weakself.backView.center;
                [weakself.backView addSubview:subMenuView];
                [weakself showSubMenuView:subMenuView];
                weakself.subMenuView = subMenuView;
            } else {
                [weakself hideBackView];
            }
        };
        
        [menuBackView addSubview:menuItemView];
    }
    
    CAShapeLayer *backMaskLayer = [[CAShapeLayer alloc] init];
    self.backMaskLayer = backMaskLayer;
    backMaskLayer.backgroundColor = [UIColor blackColor].CGColor;
    backView.layer.mask = backMaskLayer;
}

- (UIView *)renderMenuItemViewWithMenuItem:(SLDebugMenuItem *)item index:(int)index {
    CGFloat menuItemViewH = 44;
    UIView *menuItemView = [[UIView alloc] initWithFrame:CGRectMake(0, (index + 1) * menuItemViewH, _screenWidth, menuItemViewH)];
    
    UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(15, 5, _screenWidth - 30, menuItemViewH - 10)];
    titleLabel.text = item.menuTitle;
    titleLabel.font = [UIFont systemFontOfSize:15];
    titleLabel.textColor = [UIColor whiteColor];
    titleLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
    [menuItemView addSubview:titleLabel];
    
    return menuItemView;
}

#pragma mark ----- 外部方法
- (void)showDebuger {
    [self showDebugerWithPosition:CGPointZero];
}

- (void)showDebugerWithPosition:(CGPoint)point {
    if (point.x != 0 || point.y != 0) {
        self.entranceButton.center = CGPointMake(point.x - self.entranceButton.frame.size.width / 2.0, point.y - self.entranceButton.frame.size.height / 2.0);
        self.backView.center = self.entranceButton.center;
    }
    
    [[UIApplication sharedApplication].keyWindow addSubview:self.entranceButton];
}

- (void)hideBackView {
    [self backViewAnimationWithIsShow:false];
}

#pragma mark ----- 动画效果
- (void)backViewAnimationWithIsShow:(BOOL)isShow {
    
    CGFloat entranceButtonCX = self.entranceButton.center.x;
    CGFloat entranceButtonCY = self.entranceButton.center.y;
    CGFloat backMaskLayerWH = sqrt(_screenWidth * _screenWidth + _screenHeight * _screenHeight);
    UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(entranceButtonCX, entranceButtonCY, 0, 0)];
    UIBezierPath *endPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-(backMaskLayerWH - _screenWidth) / 2.0, -(backMaskLayerWH - _screenHeight) / 2.0, backMaskLayerWH, backMaskLayerWH)];
    if (!isShow) {
        startPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-(backMaskLayerWH - _screenWidth) / 2.0, -(backMaskLayerWH - _screenHeight) / 2.0, backMaskLayerWH, backMaskLayerWH)];
        endPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(entranceButtonCX, entranceButtonCY, 0, 0)];
    }
    self.backMaskLayer.path = endPath.CGPath;
    CABasicAnimation *maskAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
    if (isShow) {
        [maskAnimation setValue:@"showBack" forKey:@"animType"];
    } else {
        [maskAnimation setValue:@"hideBack" forKey:@"animType"];
    }
    maskAnimation.fromValue = (__bridge id _Nullable)(startPath.CGPath);
    maskAnimation.toValue = (__bridge id _Nullable)(endPath.CGPath);
    maskAnimation.duration = 0.35;
    maskAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    maskAnimation.delegate = self;
    [self.backMaskLayer addAnimation:maskAnimation forKey:@"hideBack"];
}

- (void)showSubMenuView:(UIView *)subView {
    subView.layer.transform = CATransform3DMakeScale(1, 1, 1);
    
    CABasicAnimation *subViewAnim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    subViewAnim.fromValue = @0;
    subViewAnim.toValue = @1;
    subViewAnim.duration = 0.25;
    subViewAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
    [subView.layer addAnimation:subViewAnim forKey:@"subviewShow"];
}

- (void)hideMenuView:(UIView *)subView {
    self.subMenuView = nil;
    self.menuBackView.alpha = 1;
    subView.layer.transform = CATransform3DMakeScale(0, 0, 0);
    
    CABasicAnimation *subViewAnim = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    subViewAnim.fromValue = @1;
    subViewAnim.toValue = @0;
    subViewAnim.duration = 0.25;
    subViewAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
    [subView.layer addAnimation:subViewAnim forKey:@"subviewShow"];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [subView removeFromSuperview];
    });
    
}

#pragma mark ----- 点击事件
- (void)entranceButtonClick:(UIButton *)sender {
    
    for (UIView *view in self.backView.subviews) {
        if (view == self.menuBackView) {
            view.alpha = 1;
        } else {
            [view removeFromSuperview];
        }
    }
    
    [[UIApplication sharedApplication].keyWindow insertSubview:self.backView belowSubview:self.entranceButton];
    
    [self backViewAnimationWithIsShow:true];
}

- (void)menuItemButtonClick:(UIButton *)sender {
    [self hideBackView];
}

#pragma mark ----- 便利方法
// 快速设置button
- (void)setButton:(UIButton *)button withTitle:(NSString *)title titleColor:(UIColor *)titleColor backgroundColor:(UIColor *)backgroundColor target:(id)target action:(SEL)action {
    [button setTitle:title forState:UIControlStateNormal];
    [button setTitleColor:titleColor forState:UIControlStateNormal];
    [button setBackgroundImage:[self imageWithColor:backgroundColor] forState:UIControlStateNormal];
    [button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
}

- (UIImage *)imageWithColor:(UIColor *)color {
    CGSize size = CGSizeMake(1, 1);
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [color CGColor]);
    CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
    UIImage * desImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return desImage;
}

#pragma mark ----- CAAnimationDelegate
- (void)animationDidStart:(CAAnimation *)anim {
    if ([@"showBack" isEqualToString:[anim valueForKey:@"animType"]]) {
        self.entranceButton.alpha = 0;
    }
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    if ([@"hideBack" isEqualToString:[anim valueForKey:@"animType"]]) {
        [self.backView removeFromSuperview];
        
        self.entranceButton.alpha = 1;
    }
}

@end

@interface SLDebugMenuItem()

@end

@implementation SLDebugMenuItem

+ (instancetype)itemWithMenuTitle:(NSString *)menuTitle action:(SLDebugClickAction)action {
    SLDebugMenuItem *item = [[SLDebugMenuItem alloc] init];
    item.menuTitle = menuTitle;
    item.action = action;
    return item;
}

@end
